package me.zhengjie.tcp;


import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.WriteSingleRegisterRequest;
import com.digitalpetri.modbus.responses.ModbusResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.WriteSingleRegisterResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import lombok.RequiredArgsConstructor;
import me.zhengjie.pojo.entity.WcsConfiguration;
import me.zhengjie.pojo.entity.WcsFluentFrameIp;
import me.zhengjie.sqlservice.WcsConfigurationService;
import me.zhengjie.sqlservice.WcsFluentFrameIpService;
import me.zhengjie.utility.state.TaskType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class FluentFrame {

    private static final Logger fluentFrameALog = LoggerFactory.getLogger("fluentFrameA");

    private final Map<String, ModbusTcpMaster> masterMap = new ConcurrentHashMap<>();

    private final WcsFluentFrameIpService wcsFluentFrameIpService;

    static ModbusTcpMaster master;

    private final ConcurrentHashMap<Integer, ReentrantLock> addressLocks = new ConcurrentHashMap<>();


    // 获取锁的方法
    private ReentrantLock getLockForAddress(int address) {
        return addressLocks.computeIfAbsent(address, key -> new ReentrantLock(true)); // 公平锁
    }




    /**
     * 获取TCP协议的Master
     */
    @PostConstruct
    public void initModbusTcpMaster() {
        try {
            if (master == null) {
                // 创建配置
                List<WcsFluentFrameIp> wcsFluentFrameIp = wcsFluentFrameIpService.selectAll();
                /* 公共单线程池：所有 master 共用，避免轰炸从站 */
                ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
                    Thread t = new Thread(r, "modbus-single");
                    t.setDaemon(true);
                    return t;
                });

                if (!wcsFluentFrameIp.isEmpty()){
                    for (WcsFluentFrameIp fluentFrameIp : wcsFluentFrameIp) {
                        String ip = fluentFrameIp.getIp();
                        Integer port = fluentFrameIp.getPort();
                        String fluentFrame = fluentFrameIp.getFluentFrame();
                        fluentFrameALog.info("A区流利架ip:{},端口：{}",ip,port);
                        ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder(ip)
                                .setPort(port)
                                .setTimeout(Duration.ofSeconds(8))
                                .setExecutor(exec)
                                .build();
                        master = new ModbusTcpMaster(config);
                        master.connect();
                        masterMap.put(fluentFrame, master);
                        fluentFrameALog.info("FluentFrame {} 连接成功 {}:{}", fluentFrame, ip, port);
                    }
                }
            }
        } catch (Exception e) {
            fluentFrameALog.error("叠合区plc连接失败，失败原因：{}", e.toString());
        }
    }

    public Integer readHoldingRegisters(String fluentFrame, int address,int unitId) {
        int attempts = 0;
        int maxRetries = 3;
        boolean acquired = false;
        ReentrantLock lock = getLockForAddress(address);
        try {
            while (attempts < maxRetries) {
                acquired = lock.tryLock(500, TimeUnit.MILLISECONDS); // 尝试获取锁
                if (acquired) {
                    ModbusTcpMaster modbusTcpMaster = masterMap.get(fluentFrame);
                    CompletableFuture<ReadHoldingRegistersResponse> future = modbusTcpMaster
                            .sendRequest(new ReadHoldingRegistersRequest(address, 2), unitId);
                    if (future !=null){
                        ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get(6, TimeUnit.SECONDS);
                        if (readHoldingRegistersResponse != null) {
                            ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                            int i = buf.readUnsignedShort();
                            ReferenceCountUtil.release(readHoldingRegistersResponse);
                            return i;
                        }
                    }
                    return null;
                }else {
                    fluentFrameALog.error("获取锁超时，第{}次重试，地址={}", attempts + 1, address);
                    attempts++; // 增加重试计数
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return null;
        } catch (InterruptedException | ExecutionException e) {
            fluentFrameALog.error("读取物流区寄存器地址值时出错，错误原因：{}", e);
            return null;
        } catch (TimeoutException e) {
            fluentFrameALog.warn("读取寄存器超时，address={}, unitId={},报错原因：{}", address, unitId,e);
            return null;
        } finally {
            if (acquired) {
                lock.unlock(); // 确保锁释放
            }
        }
    }

    public String readHoldingRegistersString(String fluentFrame, int address,int unitId) {
        int attempts = 0;
        int maxRetries = 3;
        boolean acquired = false;
        ReentrantLock lock = getLockForAddress(address);
        try {
            while (attempts < maxRetries) {
                acquired = lock.tryLock(500, TimeUnit.MILLISECONDS); // 尝试获取锁
                if (acquired) {
                    ModbusTcpMaster modbusTcpMaster = masterMap.get(fluentFrame);
                    CompletableFuture<ReadHoldingRegistersResponse> future = modbusTcpMaster
                            .sendRequest(new ReadHoldingRegistersRequest(address, 10), unitId);
                    ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();
                    if (readHoldingRegistersResponse != null) {
                        ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                        if (buf != null) {
                            // 将 ByteBuf 中的内容转换为字符串
                            String asciiString = buf.toString(StandardCharsets.US_ASCII);
                            fluentFrameALog.info("原始 ASCII 字符串: " + asciiString);

                            // 将 ASCII 字符串转换为正确的顺序
                            String correctedString = correctOrder(asciiString);
                            String s = filterInvalidChars(correctedString);
                            fluentFrameALog.info("正确的 ASCII 字符串:{}", s);
                            return s;
                        }
                    }
                }else {
                    fluentFrameALog.error("获取锁超时，第{}次重试，地址={}", attempts + 1, address);
                    attempts++; // 增加重试计数
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                }
            return null;
        } catch (InterruptedException | ExecutionException e) {
            fluentFrameALog.info(e.getMessage());
            return null;
        }finally {
            if (acquired) {
                lock.unlock(); // 确保锁释放
            }
        }

    }

    public String readHoldingRegistersString10(String fluentFrame, int address,int unitId) {
        int attempts = 0;
        int maxRetries = 3;
        boolean acquired = false;
        ReentrantLock lock = getLockForAddress(address);
        try {
            while (attempts < maxRetries) {
                acquired = lock.tryLock(500, TimeUnit.MILLISECONDS); // 尝试获取锁
                if (acquired) {
                    ModbusTcpMaster modbusTcpMaster = masterMap.get(fluentFrame);
                    CompletableFuture<ReadHoldingRegistersResponse> future = modbusTcpMaster
                            .sendRequest(new ReadHoldingRegistersRequest(address, 10), unitId);
                    ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();
                    if (readHoldingRegistersResponse != null) {
                        ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                        if (buf != null) {
                            // 将 ByteBuf 中的内容转换为字符串
                            String asciiString = buf.toString(StandardCharsets.US_ASCII);
                            fluentFrameALog.info("原始 ASCII 字符串: " + asciiString);

                            // 将 ASCII 字符串转换为正确的顺序
                            String correctedString = correctOrder(asciiString);
                            String s = filterInvalidChars(correctedString);
                            fluentFrameALog.info("正确的 ASCII 字符串:{}", s);
                            return s;
                        }
                    }
                }else {
                    fluentFrameALog.error("获取锁超时，第{}次重试，地址={}", attempts + 1, address);
                    attempts++; // 增加重试计数
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return null;
        } catch (InterruptedException | ExecutionException e) {
            fluentFrameALog.error(e.getMessage());
            return null;
        }finally {
            if (acquired) {
                lock.unlock(); // 确保锁释放
            }
        }

    }
    /*public void write(int address, int value){
        // 发送单个寄存器数据，一般是无符号16位值：比如10
        CompletableFuture<ModbusResponse> future = master.sendRequest(new WriteSingleRegisterRequest(address, value), 0);
    }*/

    private boolean writeToRegister(int address, int value,int unitId,String fluentFrame) throws Exception {
        try {
            ModbusTcpMaster modbusTcpMaster = masterMap.get(fluentFrame);
            // 发送异步写入请求
            CompletableFuture<ModbusResponse> future = modbusTcpMaster.sendRequest(new WriteSingleRegisterRequest(address, value), unitId);

            // 阻塞等待结果返回，设置超时时间
            ModbusResponse response = future.get(5000, TimeUnit.MILLISECONDS);

            // 检查响应是否为写入成功的类型
            if (response instanceof WriteSingleRegisterResponse) {
                WriteSingleRegisterResponse writeResponse = (WriteSingleRegisterResponse) response;

                // 校验返回的地址和值是否匹配
                if (writeResponse.getAddress() == address && writeResponse.getValue() == value) {
                    return true; // 写入成功
                } else {
                    fluentFrameALog.error("写入响应内容不匹配：地址={}, 值={}, 响应={}", address, value, writeResponse);
                }
            } else {
                fluentFrameALog.error("响应类型错误，预期=WriteSingleRegisterResponse，实际={}", response.getClass().getSimpleName());
            }

            return false; // 写入失败
        } catch (TimeoutException e) {
            fluentFrameALog.error("写入超时，地址={}，值={}", address, value, e);
            return false; // 写入超时
        } catch (Exception e) {
            fluentFrameALog.error("写入异常，地址={}，值={}", address, value, e);
            throw e; // 抛出异常以便外层重试逻辑处理
        }
    }

    public boolean writeWithRetry(int address, int value, int unitId, int maxRetries,String fluentFrame) {
        int attempts = 0;
        ReentrantLock lock = getLockForAddress(address); // 获取地址锁
        boolean acquired = false;
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        while (attempts < maxRetries) {
            try {
                acquired = lock.tryLock(500, TimeUnit.MILLISECONDS); // 尝试获取锁
                if (acquired) {
                    // 尝试发送写入请求并等待结果
                    fluentFrameALog.info("往PLC上写入次数：{}",attempts);
                    boolean success = writeToRegister(address, value,unitId,fluentFrame);
                    if (success) {
                        fluentFrameALog.info("写入成功：地址={}，值={}", address, value);
                        return true; // 写入成功，直接退出
                    } else {
                        fluentFrameALog.error("写入失败，地址={}，值={}", address, value);
                        try {
                            attempts++; // 增加重试计数
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                } else {
                    fluentFrameALog.error("获取锁超时，第{}次重试，地址={}", attempts + 1, address);
                    attempts++; // 增加重试计数
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 恢复中断状态
                fluentFrameALog.error("线程中断，地址={}，值={}", address, value, e);
                break; // 中断异常退出
            } catch (Exception e) {
                fluentFrameALog.error("写入异常，第{}次重试，地址={}，值={}", attempts + 1, address, value, e);
            } finally {
                if (acquired) {
                    lock.unlock(); // 确保锁释放
                }
            }
        }
        fluentFrameALog.error("写入失败，超出最大重试次数，地址={}，值={}", address, value);
        return false;
    }
    // 将 ASCII 字符串转换为正确的顺序
    public static String correctOrder(String asciiString) {
        StringBuilder correctedBuilder = new StringBuilder();
        for (int i = 0; i < asciiString.length(); i += 2) {
            // 交换每两个字节的位置
            String high = asciiString.substring(i, i + 1);
            String low = asciiString.substring(i + 1, i + 2);
            correctedBuilder.append(low).append(high);
        }
        return correctedBuilder.toString();
    }

    // 过滤掉无效的 ASCII 字符
    public static String filterInvalidChars(String input) {
        StringBuilder filteredBuilder = new StringBuilder();
        for (char c : input.toCharArray()) {
            if (c >= 32 && c <= 126) { // 有效的 ASCII 字符范围
                filteredBuilder.append(c);
            }
        }
        return filteredBuilder.toString();
    }
}
